package com.mac.hosp_pre.admin.service.impl;

import com.mac.hosp_pre.admin.service.BaseMongoService;
import com.mac.hosp_pre.admin.constant.HospConstant;
import com.mac.hosp_pre.reply.PageResult;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;

/**
 * BaseMongoServiceImpl
 * @Author: mac
 */
@Slf4j
public abstract class BaseMongoServiceImpl<T> implements BaseMongoService<T> {
    @Autowired
    protected MongoTemplate mongoTemplate;

    /**
     * 保存一个对象到mongodb
     * @param bean
     * @return
     */
    @Override
    public T save(T bean) {
        return mongoTemplate.save(bean);
    }

    /**
     * 批量保存
     * @param collection
     * @return
     */
    @Override
    public Collection<T> saveAll(Collection<T> collection) {
        return mongoTemplate.insertAll(collection);
    }

    /**
     * 根据id删除对象
     */
    @Override
    public void deleteById(String id) {
        Query query = new Query();
        query.addCriteria(Criteria.where("id").is(id));
        mongoTemplate.remove(this.findById(id));
    }


    /**
     * 根据对象的属性删除
     * @param t
     */
    @Override
    public void deleteByCondition(T t) {
        Query query = buildBaseQuery(t);
        mongoTemplate.remove(query, getEntityClass());
    }

    /**
     * 根据id进行更新
     * @param id
     * @param t
     */
    @Override
    public void updateById(String id, T t) {
        Query query = new Query();
        query.addCriteria(Criteria.where("id").is(id));
        Update update = buildBaseUpdate(t);
        update(query, update);
    }

    /**
     * 根据对象的属性查询
     * @param t
     * @return
     */
    @Override
    public List<T> findByCondition(T t) {
        Query query = buildBaseQuery(t);
        Class<T> entityClass = getEntityClass();
        if (existsDelete(entityClass,HospConstant.DELETED)){
            query.addCriteria(Criteria.where(HospConstant.DELETED).is(false));
        }
        return mongoTemplate.find(query, entityClass);
    }

    /**
     * 通过条件查询实体(集合)
     * @param query
     * @return
     */
    @Override
    public List<T> find(Query query) {
        Document fieldsObject = query.getFieldsObject();
        Class<T> entityClass = getEntityClass();
        if (fieldsObject.get(HospConstant.DELETED) == null){
            if (existsDelete(entityClass,HospConstant.DELETED)){
                query.addCriteria(Criteria.where(HospConstant.DELETED).is(false));
            }
        }
        return mongoTemplate.find(query, entityClass);
    }

    /**
     * 通过一定的条件查询一个实体
     * @param query
     * @return
     */
    @Override
    public T findOne(Query query) {
        Class<T> entityClass = getEntityClass();
        if (existsDelete(entityClass,HospConstant.DELETED)){
            query.addCriteria(Criteria.where(HospConstant.DELETED).is(false));
        }
        return mongoTemplate.findOne(query, entityClass);
    }
    @Override
    public T findOne(T t) {
        Query query = buildBaseQuery(t);
        if (existsDelete(null,HospConstant.DELETED)){
            query.addCriteria(Criteria.where(HospConstant.DELETED).is(false));
        }
        return findOne(query);
    }
    /**
     * 通过条件查询更新数据
     * @param query
     * @param update
     */
    @Override
    public void update(Query query, Update update) {
        mongoTemplate.updateMulti(query, update, this.getEntityClass());
    }

    /**
     * 通过ID获取记录
     * @param id
     * @return
     */
    @Override
    public T findById(String id) {
        return mongoTemplate.findById(id, this.getEntityClass());
    }

    /**
     * 通过ID获取记录,并且指定了集合名(表的意思)
     * @param id
     * @param collectionName
     * @return
     */
    @Override
    public T findById(String id, String collectionName) {
        return mongoTemplate.findById(id, this.getEntityClass(), collectionName);
    }

    /**
     * 通过条件查询,查询分页结果
     * @param page
     * @param query
     * @return
     */
    @Override
    public PageResult<T> findPage(PageRequest page, Query query) {
        //如果没有条件 则所有全部
        query = query==null?new Query(Criteria.where("_id").exists(true)):query;
        long count = this.count(query);
        if (count == 0) {
            return PageResult.empty();
        }
        query.skip(page.getOffset()).limit(page.getPageSize());
        if (page.getSort() != null){
            query.with(page.getSort());
        }
        List<T> rows = this.find(query);
        return new PageResult<T>(rows, count, page.getPageSize(),page.getPageNumber());
    }

    @Override
    public PageResult<T> findPageByCondition(PageRequest page, T t){
        Query query = buildBaseQuery(t);
        Class entityClass = t.getClass();
        if (existsDelete(entityClass,HospConstant.DELETED)){
            query.addCriteria(Criteria.where(HospConstant.DELETED).is(false));
        }
        return findPage(page,query);
    }
    /**
     * 求数据总和
     * @param query
     * @return
     */
    @Override
    public long count(Query query){
        Class<T> entityClass = getEntityClass();
        if (existsDelete(entityClass,HospConstant.DELETED)){
            query.addCriteria(Criteria.where(HospConstant.DELETED).is(false));
        }
        return mongoTemplate.count(query, entityClass);
    }


    /**
     * 判断实体是否存在deleted
     * @return
     */
    private boolean existsDelete(Class clazz,String fieldName) {
        if (clazz == null){
            clazz = getEntityClass();
        }
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            try {
                if (field.getName().equalsIgnoreCase(fieldName)) {
                    return true;
                }
            } catch (IllegalArgumentException e) {
                log.error("获取实体类属性参数失败:",e);
            }
        }
        return false;
    }

    /**
     * 根据vo构建查询条件Query
     * @param t
     * @return
     */
    private Query buildBaseQuery(T t) {
        Query query = new Query();

        Field[] fields = t.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(t);
                if (value != null) {
                    query.addCriteria(Criteria.where(field.getName()).is(value));
                }
            } catch (IllegalArgumentException e) {
                log.error("获取实体类属性参数失败:",e);
            } catch (IllegalAccessException e) {
                log.error("反射获取实体类属性失败:",e);
            }
        }
        return query;
    }

    /**
     * 根据vo构建更新条件Query
     * @param t
     * @return
     */
    private Update buildBaseUpdate(T t) {
        Update update = new Update();
        Field[] fields = t.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(t);
                if (value != null) {
                    update.set(field.getName(), value);
                }
            } catch (Exception e) {
               log.error("获取实体类属性失败:",e);
            }
        }
        return update;
    }

    /**
     * 获取需要操作的实体类class
     * @return
     */
    @SuppressWarnings("unchecked")
    protected Class<T> getEntityClass() {
        return getSuperClassGenricType(getClass(),0);
    }

    /**
     * 获取MongoDB模板操作
     * @return
     */
    @Override
    public MongoTemplate getMongoTemplate() {
        return mongoTemplate;
    }

    private Class getSuperClassGenricType(final Class clazz, final int index){
        Type genType = clazz.getGenericSuperclass();

        if (!(genType instanceof ParameterizedType)) {
            log.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
            return Object.class;
        }

        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

        if (index >= params.length || index < 0) {
            log.warn("classType Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
                    + params.length);
            return Object.class;
        }
        if (!(params[index] instanceof Class)) {
            log.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
            return Object.class;
        }

        return (Class) params[index];
    }


}